TypeScript の const Type Parameters ってなんだよ
TypeScript: Documentation - TypeScript 5.0
TypeScriptでジェネリクスと配列で戻り値のキーを制限する | みどりのさるのエンジニア
AI が書いてきたコードが理解できなかったので勉強する
code:ts
type HasNames = { names: readonly string[] };
function getNamesExactly<T extends HasNames>(arg: T): T"names" {
return arg.names;
}
// Inferred type: string[]
const names = getNamesExactly({ names: "Alice", "Bob", "Eve" });
⬆️でもいい感じに推論してくれて string[] を得ているけど、ちょっと曖昧
本当は readonly ["Alice", "Bob", "Eve"] が欲しい
そこで⬇️のコード、as const を引数のオブジェクトにつければいい
code:ts
// Inferred type: readonly "Alice", "Bob", "Eve"
const names = getNamesExactly({ names: "Alice", "Bob", "Eve" } as const);
だけどこの書き方は面倒だし忘れちゃう
This can be cumbersome and easy to forget.
TypeScript 5.0 からは const 修飾子を型引数に使用できる
code:ts
type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T"names" {
return arg.names;
}
// Inferred type: readonly "Alice", "Bob", "Eve"
// NOTE: Didn't need to write as const here
const names = getNamesExactly({ names: "Alice", "Bob", "Eve" });
ただし、const 修飾子は mutable な値を拒否しないし、immutable な型制約も要求しないことに注意が必要
mutable な配列型を型制約に用いるとせっかくの const 推論が潰れてしまうことになる
code:ts
declare function fnBad<const T extends string[]>(arg: T): void;
// 'T' is still 'string[]' since 'readonly "a", "b", "c"' is not assignable to 'string[]'
fnBad("a", "b", "c");
declare function fnGood<const T extends readonly string[]>(arg: T): void;
// T is readonly "a", "b", "c"
fnGood("a", "b", "c");
TypeScript コンパイラがまず T を T = readonly ["a", "b", "c"] として推論しようとする
(const type parameter なのでリテラルまたはタプルで取りたがる)
しかし型制約が string[] と mutable
readonly ["a", "b", "c"] は string[] に代入できない
推論に失敗
推論は型制約側にフォールバックして T = string[] になる